/*
 * Copyright (c) 2009, Hamish Morgan. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the University of Sussex nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package locusts.client.diamondtouch;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Class for holding information about a single "segments" OSC message
 * from DTBridge and DiamondTouch table. This denotes all the individual
 * possible contact points, over a threshold, for a given user. Note that this
 * data is highly ambiguouse and will likely contain incorrect contact points.
 *
 * Protocol:
 *
 * String:      name    (/DTSegments)
 * Integer:     table width   (eg 2032)
 * Integer:     table height  (eg 1520)
 * Integer:     userId  (0...7)
 * Integer:     no. x segments  (0..n)
 * Integer:     no. segments  (0..m)
 * For all x segments 0..n then y segments 0..m
 *      Integer first
 *      Integer last
 *      Integer maxSignal
 *      Integer peak (first <= peak <= last)
 *      Integer secondPeak (as peak, if not then -1)
 *
 * @author Hamish Morgan
 */
public class DTSegmentsEvent extends DTEvent {

    private final Segment[] xSegs;
    private final Segment[] ySegs;

    protected DTSegmentsEvent(DTHandler source,
            int userId, long time,
            Segment[] xSegs, Segment[] ySegs) {
        super(source, userId, time);
        this.xSegs = xSegs;
        this.ySegs = ySegs;
    }

    protected DTSegmentsEvent(DTSegmentsEvent other) {
        super(other);
        this.xSegs = other.xSegs;
        this.ySegs = other.ySegs;
    }

    public int numXSegs() {
        return xSegs.length;
    }

    public int numYSegs() {
        return ySegs.length;
    }

    public Segment[] getXSegs() {
        return xSegs;
    }

    public Segment[] getYSegs() {
        return ySegs;
    }

    public List<Point> getPoints() {
        final List<Point> result = new ArrayList<Point>(
                xSegs.length * ySegs.length);
        for (int i = 0; i < xSegs.length; i++) {
            final Segment x = xSegs[i];
            for (int j = 0; j < ySegs.length; j++) {
                final Segment y = ySegs[i];
                result.add(new Point(x.peak, y.peak));
                if (y.isDouble())
                    result.add(new Point(x.peak, y.secondPeak));
            }
            if (x.isDouble()) {
                for (int j = 0; j < ySegs.length; j++) {
                    final Segment y = ySegs[i];
                    result.add(new Point(x.secondPeak, y.peak));
                    if (y.isDouble())
                        result.add(new Point(x.secondPeak, y.secondPeak));
                }
            }
        }
        return result;

    }

    @Override
    public DTSegmentsEvent clone() {
        return new DTSegmentsEvent(this);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        final DTSegmentsEvent other = (DTSegmentsEvent) obj;
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + Arrays.deepHashCode(this.xSegs);
        hash = 97 * hash + Arrays.deepHashCode(this.ySegs);
        return hash;
    }

    
    @Override
    public String toString() {
        final StringBuffer buf = new StringBuffer();
        buf.append(super.toString());
        buf.append(" [");
        buf.append("Size: ").append(xSegs.length).append("x").append(
                ySegs.length);
        buf.append(", X-Segments: (");
        for (Segment seg : xSegs)
            buf.append(seg);
        buf.append("), Y-Segments: (");
        for (Segment seg : ySegs)
            buf.append(seg);
        buf.append(")]");
        return buf.toString();
    }

    public static class Segment {

        private static final transient int NO_SECOND_PEAK = -1;
        //
        private final int first;
        private final int last;
        private final int maxSignal;
        private final int peak;
        private final int secondPeak;

        public Segment(int first, int last, int maxSignal, int peak,
                int secondPeak) {
            this.first = first;
            this.last = last;
            this.maxSignal = maxSignal;
            this.peak = peak;
            this.secondPeak = secondPeak;
        }

        protected Segment(Segment other) {
            this(other.first, other.last, other.maxSignal, other.peak,
                    other.secondPeak);
        }

        public int getFirst() {
            return first;
        }

        public int getLast() {
            return last;
        }

        public int getMaxSignal() {
            return maxSignal;
        }

        public int getPeak() {
            return peak;
        }

        public int getSecondPeak() {
            return secondPeak;
        }

        public boolean isDouble() {
            return secondPeak != NO_SECOND_PEAK;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) return false;
            if (getClass() != obj.getClass()) return false;
            final Segment other = (Segment) obj;
            if (this.first != other.first) return false;
            if (this.last != other.last) return false;
            if (this.maxSignal != other.maxSignal) return false;
            if (this.peak != other.peak) return false;
            if (this.secondPeak != other.secondPeak) return false;
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 19 * hash + this.first;
            hash = 19 * hash + this.last;
            hash = 19 * hash + this.maxSignal;
            hash = 19 * hash + this.peak;
            hash = 19 * hash + this.secondPeak;
            return hash;
        }

        @Override
        public String toString() {
            final StringBuffer buf = new StringBuffer();
            buf.append('[');
            buf.append("first=").append(first);
            buf.append(", last=").append(first);
            buf.append(", maxSignal=").append(first);
            buf.append(", peak=").append(first);
            buf.append(", secondPeak=").append(
                    isDouble() ? first : "none");
            buf.append(']');
            return buf.toString();
        }

        @Override
        public Segment clone() {
            return new Segment(this);
        }
    }
}
